#pragma once

#include <vex/core/implementation/handle_from_base.h>
#include <vex/core/contract/memory_resource.h>
#include <vex/core/any_allocator.h>
#include <vex/core/as_any.h>
#include <type_traits>

namespace vex { namespace core {

#ifdef VEX_HAS_CPP_11_FEATURE_SET_3

    template < class TBase, class TAllocator, class ...TArgs >
    handle_from_base<TBase>* allocate_raw_handle(TAllocator&& /*p_alloc*/, TArgs&& .../*p_args*/)
    {
        throw std::runtime_error("allocate_raw_handle not yet implemented!");
        return nullptr;
    }

    template < class TBase, class ...TArgs >
    handle_from_base<TBase>* allocate_raw_handle(contract::memory_resource* /*p_mem_res*/, TArgs&& .../*p_args*/)
    {
        throw std::runtime_error("allocate_raw_handle not yet implemented!");
        return nullptr;
    }
#else
    template < class TBase, class TAllocator > TBase* allocate_raw_handle(TAllocator&& p_alloc)
    {
        if (auto t_memory = p_alloc.allocate(1)) {
            try {
                if (auto t_base = new(t_memory) handle_from_base<TBase>(
                    std::allocator_arg, std::forward<TAllocator>(p_alloc))) 
                {
                    return t_base;
                }
            } catch (...) {
                p_alloc.deallocate(t_memory, 1);
            }
        }

        return nullptr;
    }

    template < class TBase > TBase* allocate_raw_handle(contract::memory_resource* p_memory)
    {
        if (p_memory) {
            any_allocator< handle_from_base<TBase> > t_alloc(p_memory, true);
            return allocate_raw_handle<TBase>(std::move(t_alloc));
        }

        return nullptr;
    }

    template < class TBase, class TAllocator, class TArg1 >
    handle_from_base<TBase>* allocate_raw_handle(TAllocator&& p_alloc, 
        TArg1&& p_arg1)
    {
        throw std::runtime_error("allocate_raw_handle not yet implemented!");
        return nullptr;
    }

    template <class TBase, class TAllocator, class TArg1, class TArg2>
    handle_from_base<TBase>* allocate_raw_handle(TAllocator&& p_alloc, 
        TArg1 && p_arg1, TArg2 && p_arg2) 
    {
        throw std::runtime_error("allocate_raw_handle not yet implemented!");
        return nullptr;
    }

    template <class TBase, class TAllocator, class TArg1, class TArg2, class TArg3>
    handle_from_base<TBase>* allocate_raw_handle(TAllocator&& p_alloc, 
        TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3) 
    {
        throw std::runtime_error("allocate_raw_handle not yet implemented!");
        return nullptr;
    }

    template <class TBase, class TAllocator, class TArg1, class TArg2, class TArg3, class TArg4>
    handle_from_base<TBase>* allocate_raw_handle(TAllocator&& p_alloc, 
        TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4) 
    {
        throw std::runtime_error("allocate_raw_handle not yet implemented!");
        return nullptr;
    }
#endif
}}
